#include "stdafx.h"

#include "sensor_editor.h"

#include "common.h"


using namespace std;
static const QColor indexUsedColor = QColor(167, 211, 92);
static const int border_width = 2;
static const QColor fill_color(Qt::white);
static const QColor border_color(QColor(195, 195, 195));
static const QColor text_color(QColor(26, 40, 60));
static const QRectF sensor_ellipse = QRectF(-7, -7, 14, 14);
static const int sensor_line = 9;

SensorEditor::SensorEditor(QWeakPointer<Common> _common, QWeakPointer<SceneView> _scene) : common(_common), scene(_scene)
	//: QDialog(pwgt, Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
{
	ui.setupUi(this);
	connect(ui.bAdd, SIGNAL(clicked), this, SLOT(on_bAdd_clicked));
	connect(ui.bRemove, SIGNAL(clicked), this, SLOT(on_bRemove_clicked));
	connect(ui.listOfSensor, SIGNAL(currentRowChanged(int)), this, SLOT(displayParameters(int)));
	connect(ui.listOfSensor, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(itemClicked(QListWidgetItem*)));
	connect(ui.listOfParameter, SIGNAL(itemChanged(QListWidgetItem*)), this, SLOT(parameterStateChanged(QListWidgetItem*)));
	connect(ui.listOfParameter, SIGNAL(currentRowChanged(int)), this, SLOT(displayModel(int)));
	Units *unit_c = new Units;
	parameters = unit_c->getCalcParameters();
}

void SensorEditor::on_bAdd_clicked(){
	bool ok;
	QString name = QInputDialog::getText(this, tr("Creating sensor..."),
		tr("Enter a sensor name"), QLineEdit::Normal,  "sensor", &ok);
	if(ok){
		if(name.isEmpty()){
			QMessageBox::critical(0, tr("Error"), tr("Empty name is not valid!"), QMessageBox::Ok);	
			return;
		}
		for (int i = 0; i < ui.listOfSensor->count(); i++)
			if(ui.listOfSensor->item(i)->text() == name){
				QMessageBox::critical(0, tr("Error"), tr("That name already exists!"), QMessageBox::Ok);	
				return;
			}
		Sensor* sensor = new Sensor(common, scene, name, parameters);
		connect(sensor, SIGNAL(updatePosition(Sensor*)), pipe, SLOT(updateSensorPosition(Sensor*)));

		QSharedPointer<Indicator> indicator = QSharedPointer<Indicator>(new Indicator(common, scene, sensor, QPointF(0, 50), false, parameters));
		indicator->setIndicatorVisible(true);
		sensor->setIndicator(indicator);
		connect(sensor, SIGNAL(updateName(const QString &)), this, SLOT(updateSensorName(const QString &)));
		list_of_sensor.push_back(sensor);
		ui.listOfSensor->addItem(name);
		ui.listOfSensor->setCurrentRow(ui.listOfSensor->count() - 1);
		displayParameters(ui.listOfSensor->currentRow());	
	}
}

void SensorEditor::on_bRemove_clicked(){
	if (ui.listOfSensor->currentRow() == -1){
		QMessageBox::information(0, tr("Removing sensor..."), tr("Sensor is not selected!"), QMessageBox::Ok);	
		return;
	}
	if(QMessageBox::question(0, tr("Removing sensor..."), QString(tr("Remove the sensor: '%1'?")).arg(ui.listOfSensor->currentItem()->text()),
		QMessageBox::Yes |QMessageBox::No) == QMessageBox::Yes)
	{	
		int sensor_index = ui.listOfSensor->currentRow();
		delete ui.listOfSensor->takeItem(sensor_index);
		delete list_of_sensor[sensor_index];
		list_of_sensor.erase(list_of_sensor.begin() + sensor_index);
		
		displayParameters(ui.listOfSensor->currentRow());	
	}
}

void SensorEditor::displayParameters(int index){
	if (index == -1){
		ui.listOfParameter->clear();
		return;
	}
	ui.listOfParameter->clear();
	vector<pair<QString, bool>> list = list_of_sensor[index]->getListOfParameter();
	for (int i = 0; i < list.size(); i++){
		QListWidgetItem *item = new QListWidgetItem(list[i].first);
		if (list[i].second) item->setCheckState(Qt::Checked);
		else item->setCheckState(Qt::Unchecked);
		item->setTextColor(list[i].second ? Qt::black : Qt::gray);
		ui.listOfParameter->addItem(item);
	}
	setModel(list_of_sensor[ui.listOfSensor->currentRow()]->getModel());
	ui.trParameters->setEnabled(true);
	common.data()->updateScript(ui.trParameters);
}


void SensorEditor::itemClicked(QListWidgetItem *item){
	displayParameters(ui.listOfSensor->currentRow());
}

void SensorEditor::parameterStateChanged(QListWidgetItem *item){
	QString name = item->text();
	bool state = item->checkState() == Qt::Checked;
	list_of_sensor[ui.listOfSensor->currentRow()]->setParameterState(name, state);
	ui.trParameters->setEnabled(state);

	ui.listOfParameter->setCurrentItem(item);
	displayModel(ui.listOfSensor->currentRow());
	item->setTextColor(state ? Qt::black : Qt::gray);
}

void SensorEditor::displayModel(int index){
	if (index == -1){
		setModel(QSharedPointer<ParametersModel>(new ParametersModel()));
		return;
	}
	QString parameter_name = ui.listOfParameter->currentItem()->text();
	if (!parameters.filter(parameter_name).count())
		return;
	setModel(list_of_sensor[ui.listOfSensor->currentRow()]->getParameterModel(parameter_name));

	bool state = list_of_sensor[ui.listOfSensor->currentRow()]->getState(parameter_name);
	ui.trParameters->setEnabled(state);
	common.data()->updateScript(ui.trParameters);

}

void SensorEditor::setModel(QSharedPointer<ParametersModel> _model){
	ui.trParameters->setModel(_model.data());
	ui.trParameters->setItemDelegate(new Delegate(ui.trParameters));
	ui.trParameters->setStyleSheet("alternate-background-color: rgb(190, 234, 235)");
	for(int i = 3; i <= 7; i++)
		ui.trParameters->hideColumn(i);
	ui.trParameters->header()->setStretchLastSection(false);
	ui.trParameters->header()->setResizeMode(1, QHeaderView::Stretch);   
	ui.trParameters->setColumnWidth(0, 120);
	ui.trParameters->setColumnWidth(2, 40);
}

void SensorEditor::setData(vector<Sensor*> _list_of_sensor, ItemScene* _pipe){
	pipe = _pipe;
	list_of_sensor = _list_of_sensor;
	foreach(Sensor* sensor, list_of_sensor){
		ui.listOfSensor->addItem(sensor->getName());
		connect(sensor, SIGNAL(updateName(const QString &)), this, SLOT(updateSensorName(const QString &)));
	}
	if (ui.listOfSensor->count()) ui.listOfSensor->setCurrentRow(0);
}

void SensorEditor::setSensor(Sensor *_sensor){
	list_of_sensor.push_back(_sensor);
	ui.listOfSensor->addItem(_sensor->getName());
	connect(_sensor, SIGNAL(updateName(const QString &)), this, SLOT(updateSensorName(const QString &)));
	ui.listOfSensor->setCurrentRow(0);
	ui.bAdd->setVisible(false);
	ui.bRemove->setVisible(false);
}

vector<Sensor*> SensorEditor::getData(){
	return list_of_sensor;
}

void SensorEditor::updateSensorName(const QString &name){
	ui.listOfSensor->currentItem()->setText(name);
}

//=================================================================================================================
Sensor::Sensor(QWeakPointer<Common> _common, QWeakPointer<SceneView> _scene, const QString &_name, const QStringList &_parameters)
	: common(_common), scene(_scene), name(_name)
{
	init_model();
	foreach(QString p, _parameters){
		list_of_parameters[p.toStdString()] = make_pair(init_parameter_model(p), false);
	}
	vector<QString> list_of_script = scene.data()->common.data()->getScriptList();
	foreach(QString script_name, list_of_script){
		addScriptParameter(script_name);
	}
	scene.data()->addItem(this);
}
Sensor::~Sensor(){
	common.clear();
	scene.clear();	
	model.clear();
	indicator.clear();
}


void Sensor::init_model(){
	//0 - combobox/boolean	//1 - spinbox
	//2 - textbox	//3 - label
	QString common_parameter = "COMMON";
	model = QSharedPointer<ParametersModel>(new ParametersModel(this));
	model->addItem(common_parameter, "NAME", name, "", 2, 0,"name");
	model->addItem(common_parameter, "COORDINATE", 0, "m", 1, 0,"coordinate");
	model->addItem(common_parameter, "DISPLAYED", true, "-", 0, 0,"show");

	connect(this, SIGNAL(setNewParameterValue(const QString &, const QVariant &)), 
		model.data(), SLOT(changeData(const QString &, const QVariant &)));

	connect(model.data(), SIGNAL(ItemChanged(const QString &, const QVariant &)),
		this, SLOT(getNewParameterValue(const QString &, const QVariant &)));

	connect(common.data(), SIGNAL(scriptAdded(const QString&)), this, SLOT(addScriptParameter(const QString&)));
	connect(common.data(), SIGNAL(scriptRemoved(const QString&)), this, SLOT(removeScriptParameter(const QString&)));
}

QSharedPointer<ParametersModel> Sensor::init_parameter_model(const QString &_parameter){
	QSharedPointer<ParametersModel> model(new ParametersModel(this));
	QString unit = common.data()->getDefaultUnitText(_parameter);
	//QString unit_time = common.data()->getDefaultUnitText("TIME");
	QString common_parameter = "COMMON";
	model->addItem(common_parameter, "DIGITS", 2, "", 1, 0,"number of digits after the decimal point");
	model->addItem(common_parameter, "UNIT", unit, "", 2, 0,"default unit");
	return model;
}


void Sensor::addScriptParameter(const QString &script_name){	
	QString unit_time = common.data()->getDefaultUnitText("TIME");
	for (hash_map<string, pair<QSharedPointer<ParametersModel>, bool> >::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
		ParametersModel *_model = it->second.first.data();
		QString unit = common.data()->getDefaultUnitText(QString::fromStdString(it->first));
		_model->addItem(script_name, "A", QString("both:piecewise-constant/TIME:ERROR/s:%1/").arg(unit), "edit", 5, 0);
		_model->addItem(script_name, "T", QString("both:piecewise-constant/TIME:ERROR/s:%1/").arg(unit_time), "edit", 5, 0);
		_model->addItem(script_name, "B", QString("both:piecewise-constant/TIME:ERROR/s:%1/").arg(unit), "edit", 5, 0);
		_model->addItem(script_name, "C", QString("both:piecewise-constant/TIME:ERROR/s:%1/").arg(unit), "edit", 5, 0);
	}

}

void Sensor::removeScriptParameter(const QString &script_name){
	for (hash_map<string, pair<QSharedPointer<ParametersModel>, bool> >::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		it->second.first.data()->removeItems(script_name);
}

void Sensor::update_parameters(){
	common.data()->getParametersWidget().data()->setItemDelegate(new Delegate());
}

QSharedPointer<ParametersModel> Sensor::getParameterModel(QString _name){
	return list_of_parameters[_name.toStdString()].first;
}

bool Sensor::getState(QString _name){
	return list_of_parameters[_name.toStdString()].second;
}


vector<pair<QString, bool>> Sensor::getListOfParameter() {
	vector<pair<QString, bool>> list;

	for (hash_map<string, pair<QSharedPointer<ParametersModel>, bool> >::const_iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
		QString name = QString::fromStdString(it->first);
		bool checked = it->second.second;
		list.push_back(make_pair(name, checked));
	}
	return list;
}

void Sensor::setParameterState(const QString &_name, const bool &_state){
	for (hash_map<string, pair<QSharedPointer<ParametersModel>, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if(QString::fromStdString(it->first) == _name){
			it->second.second = _state;
			break;
		}
}

void Sensor::getNewParameterValue(const QString &_name, const QVariant &_value){
	if (_name == "NAME"){
		name = _value.toString();
		emit updateName(name);
	}
}

QRectF Sensor::boundingRect() const{
	return QRectF(sensor_ellipse.x(), sensor_ellipse.y(), sensor_ellipse.width(), sensor_ellipse.height() + sensor_line);
}

void Sensor::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *){
	painter->setRenderHint(QPainter::Antialiasing);
	QPen pen;
	pen.setWidth(border_width);
	pen.setColor(border_color);
	painter->setPen(pen);
	painter->setBrush(fill_color);
	painter->drawEllipse(sensor_ellipse);
	painter->drawLine(QPoint(0, sensor_ellipse.y() + sensor_ellipse.height()), QPointF(0, sensor_ellipse.y() + sensor_ellipse.height() + sensor_line));
}

void Sensor::adjust(const QPointF &_pos){
	setPos(QPointF(_pos.x(), _pos.y() - (sensor_ellipse.height()/2 + sensor_line)));
	if(!indicator.isNull()) indicator->adjust(this->pos());
}


void Sensor::contextMenuEvent(QGraphicsSceneContextMenuEvent *event){
	QMenu menu;
	bool enabledAct = countCalcParameters();
	QScopedPointer<QAction> actIndicator(new QAction("indicator", this));
	connect(actIndicator.data(), SIGNAL(triggered()), this, SLOT(showIndicator()));
	actIndicator.data()->setCheckable(true);
	actIndicator.data()->setChecked(indicator->isVisible());
	actIndicator.data()->setEnabled(enabledAct);
	QScopedPointer<QAction> actIndicatorLine(new QAction(/*auto-hide*/"line", this));
	connect(actIndicatorLine.data(), SIGNAL(triggered()), this, SLOT(showIndicatorLine()));
	actIndicatorLine.data()->setCheckable(true);
	actIndicatorLine.data()->setChecked(indicator->lineIsVisible());
	actIndicatorLine.data()->setEnabled(indicator->isVisible());
	actIndicatorLine.data()->setEnabled(enabledAct);
	QScopedPointer<QAction> actEdit(new QAction("edit...", this));
	connect(actEdit.data(), SIGNAL(triggered()), this, SLOT(showSensorEditor()));
	menu.addAction(actIndicator.take());
	menu.addAction(actIndicatorLine.take());
	menu.addAction(actEdit.take());
	menu.exec(event->screenPos());
}


void Sensor::showIndicator(){
	bool d = indicator->isVisible();
	indicator->setIndicatorVisible(!indicator->isVisible());
}

void Sensor::showIndicatorLine(){
	indicator->setLineVisible(!indicator->lineIsVisible() && indicator->isVisible());
}

void Sensor::showSensorEditor(){
	SensorEditor sensor_editor(common, scene);
	sensor_editor.setSensor(this);
	if (sensor_editor.exec()){
		/*Sensor* temp = sensor_editor.getData()[0];
		this = temp.data();*/
		if (isVisibleOnScene())
			scene.data()->addItem(this);
		else
			scene.data()->removeItem(this);
		QStringList calculatedParameters = getCalculatedParameters();
		QStringList units_for_calculatedParameters;
		QVector<int> digits;
		foreach(QString parameter, calculatedParameters){
			units_for_calculatedParameters.push_back(getUnit(parameter.toStdString()));
			digits.push_back(getDigits(parameter.toStdString()));
		}
		indicator->updateParameters(calculatedParameters, units_for_calculatedParameters, digits);
		emit updatePosition(this);
	}
}

double Sensor::getParameterError(const string &_parameter, const QString &_script, const double &_time){
	double t = list_of_parameters[_parameter].first.data()->getTimeVaryingParameter("T", _time, _script);
	double _sin = abs(t) < 1e-2 ? 0 : sin(2 * 3.14 * _time / t );
	double a = list_of_parameters[_parameter].first.data()->getTimeVaryingParameter("A", _time, _script);
	double b = list_of_parameters[_parameter].first.data()->getTimeVaryingParameter("B", _time, _script);
	double c = list_of_parameters[_parameter].first.data()->getTimeVaryingParameter("C", _time, _script);
	double rnd = rand() / 32768.0 * 2 - 1;
	
	double error = a * _sin + b + c * rnd; 
	return error;
}

QStringList Sensor::getCalculatedParameters(){
	QStringList list;
	for (hash_map<string, pair<QSharedPointer<ParametersModel>, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if (it->second.second)
			list.push_back(QString::fromStdString(it->first));
	return list;
}

void Sensor::createTrend(const QString &_parameter){
	common.data()->addSensorTrend(this, _parameter);
}

int Sensor::countCalcParameters(){
	int count = 0;
	for (hash_map<string, pair<QSharedPointer<ParametersModel>, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++)
		if(it->second.second)
			count++;
	return count;
}

QSharedPointer<ParametersModel> Sensor::addParameter(const QString &_name, const bool &_calculated){ 
	bool parameter_exist = false;
	for (hash_map<string, pair<QSharedPointer<ParametersModel>, bool> >::iterator it = list_of_parameters.begin(); it != list_of_parameters.end(); it++){
		if(it->first == _name.toStdString()){
			parameter_exist = true;
			break;
		}
	}
	QSharedPointer<ParametersModel> model;
	if (parameter_exist){
		model = list_of_parameters[_name.toStdString()].first;
	}else{
		model = init_parameter_model(_name);
		vector<QString> script_list = common.data()->getScriptList();
		QString unit_time = common.data()->getDefaultUnitText("TIME");
		foreach(QString script_name, script_list){
			QString unit = common.data()->getDefaultUnitText(_name);
			model->addItem(script_name, "A", QString("both:piecewise-constant/TIME:ERROR/s:%1/").arg(unit), "edit", 5, 0);
			model->addItem(script_name, "T", QString("both:piecewise-constant/TIME:ERROR/s:%1/").arg(unit_time), "edit", 5, 0);
			model->addItem(script_name, "B", QString("both:piecewise-constant/TIME:ERROR/s:%1/").arg(unit), "edit", 5, 0);
			model->addItem(script_name, "C", QString("both:piecewise-constant/TIME:ERROR/s:%1/").arg(unit), "edit", 5, 0);
		}
	}
	list_of_parameters[_name.toStdString()] = make_pair(model, _calculated);
	return model;
}